using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Runtime.Common.Utility;
using UnityEditor;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.AddressableAssets.Settings.GroupSchemas;
using UnityEngine;
using UnityEngine.U2D;

namespace FormicArch.AssetBundleEditor
{
    public class AssetBuildManager : IAssetBuildManager
    {
        private readonly string assetbundlePath = "Assets/Res";
        private readonly string assetBundlePathPath = "Assets/AssetbundleBuildConfig/AssetBundlePathData.asset";
        private AssetBundlePath assetBundlePath;

        AssetBundlePath CurrentAssetBundlePath
        {
            get
            {
                if (!assetBundlePath)
                {
                    assetBundlePath = AssetDatabase.LoadAssetAtPath<AssetBundlePath>(assetBundlePathPath);
                }

                return assetBundlePath;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private readonly Dictionary<string, IAssetDependency> buildAssetDect = new();

        /// <summary>
        /// 图集依赖
        /// </summary>
        private readonly Dictionary<string, IAssetDependency> buildAtlasAssetDect = new();

        private readonly string dependencyName = "Dependency";


        // public bool TryAdd(string path, out IAssetDependency dependency)
        // {
        //     if (!buildAssetDect.TryGetValue(path, out dependency))
        //     {
        //         buildAssetDect.Add(path, dependency = new CommonDependency(path, this));
        //         return false;
        //     }
        //
        //     return true;
        // }

        public void AddDependency(string path, IAssetDependency dependency)
        {
            buildAssetDect.Add(path, dependency);
        }


        public bool TryGetDependencyValue(string path, out IAssetDependency dependency)
        {
            return buildAssetDect.TryGetValue(path, out dependency);
        }

        public List<string> GetDependencies(string path)
        {
            if (AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(SpriteAtlas))
            {
                return new List<string>();
            }

            var dependencies = AssetDatabase.GetDependencies(path, false);
            HashSet<string> atlasPathDict = new();

            foreach (var dependencyPath in dependencies)
            {
                if (AssetDatabase.IsValidFolder(dependencyPath)
                    || IsFilter(dependencyPath) //过滤掉不需要打包的资源
                    || path == dependencyPath //自己依赖自己
                   )
                {
                    continue;
                }

                if (AssetDatabase.GetMainAssetTypeAtPath(dependencyPath) == typeof(Texture2D))
                {
                    if (buildAtlasAssetDect.TryGetValue(dependencyPath, out var temp))
                    {
                        atlasPathDict.Add(temp.AssetPath);
                        continue;
                    }
                }

                atlasPathDict.Add(dependencyPath);
            }

            var dependList = new List<string>(atlasPathDict);
            return dependList;
        }

        public void AddAtlasDependencyDict(string path, IAssetDependency dependency)
        {
            if (!buildAtlasAssetDect.TryGetValue(path, out var temp))
                buildAtlasAssetDect.Add(path, dependency);
            else
                Debug.LogError($"存在多个图集{temp.AssetPath}======>{dependency.AssetPath}资源依赖同一个资源{path}");
        }

        public string GetGroupName(string path)
        {
            path = path.Replace("/", "_");
            path = path.Replace(".", "_");
            return path;
        }

        public string GetAddressPath(string path)
        {
            return path;
            return path.Substring(assetbundlePath.Length + 1);
        }

        public bool IsMainAsset(string assetPath)
        {
            return assetPath.StartsWith(assetbundlePath);
        }

        public void GetBuildAsset()
        {
            GetLocalBuildAsset(AssetbundleLoadType.NetRealTime);
            GetLocalBuildAsset(AssetbundleLoadType.Preload);
            GetLocalBuildAsset(AssetbundleLoadType.Streaming);
        }

        public void GetLocalBuildAsset(AssetbundleLoadType loadType)
        {
            List<AssetbundleClassify> classifies = CurrentAssetBundlePath.GetClassifyListLoadType(loadType);
            if (classifies == null)
            {
                return;
            }

            var assetPaths = AssetDatabase.FindAssets("t:Object", classifies.Select(p => p.AssetPath).ToArray());
            assetPaths = Array.ConvertAll(assetPaths, p => AssetDatabase.GUIDToAssetPath(p));
            foreach (var path in assetPaths)
            {
                if (AssetDatabase.IsValidFolder(path)
                    || IsFilter(path)
                    || buildAtlasAssetDect.ContainsKey(path)
                   )
                {
                    continue;
                }

                new CommonDependency(path, this, true) { LoadType = AssetbundleLoadType.Local }.SetLoadType(loadType);
            }

            foreach (var value in buildAssetDect)
                Debug.Log(
                    $"========{value.Key}=====Dependencies：{value.Value.Dependencies.Count}=====DependedOn：{value.Value.DependedOn.Count}");
        }
        
        private List<IAssetFilter> filterList;

        private List<IAssetFilter> FilterList
        {
            get
            {
                if (filterList == null)
                {
                    filterList = new List<IAssetFilter>();
                    foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
                    {
                        foreach (var type in assembly.GetTypes())
                        {
                            if (type.ISDescendFrom(typeof(IAssetFilter)))
                            {
                                filterList.Add(Activator.CreateInstance(type) as IAssetFilter);
                            }
                        }
                    }
                }

                return filterList;
            }
        }

        /// <summary>
        /// 过滤不需要的打包资源
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private bool IsFilter(string path)
        {
            foreach (var filter in FilterList)
            {
                if (filter.IsFilter(path))
                {
                    return true;
                }
            }

            return false;
        }


        public void GetAtlasAsset()
        {
            var assetPaths = AssetDatabase.FindAssets("t:SpriteAtlas");
            assetPaths = Array.ConvertAll(assetPaths, p => AssetDatabase.GUIDToAssetPath(p));

            foreach (var path in assetPaths)
            {
                var dependency = new AtlasDependency(path, this, true);
                var dependencyies = AssetDatabase.GetDependencies(path, true);
                foreach (var dependencyPath in dependencyies)
                {
                    if (AssetDatabase.IsValidFolder(dependencyPath) || path == dependencyPath) continue;

                    dependency.AddDependency(new AssetDependency(dependencyPath, this));
                    AddAtlasDependencyDict(dependencyPath, dependency);
                }
            }
        }


        public void SetAddressable()
        {
            var settings = AddressableAssetSettingsDefaultObject.Settings;
            Dictionary<string, GroupInfo> groupInfos = GetGroupInfos(settings);

            foreach (var build in buildAssetDect)
            {
                var dependency = build.Value;
                string groupName = GetAddressGroupName(dependency);
                if (dependency.IsMainAsset)
                {
                    if (groupInfos.TryGetValue(groupName, out var groupInfo))
                    {
                        groupInfo.GetOrCreateEntry(build.Key, build.Value.GetAddressPath());
                    }

                    continue;
                }

                if (dependency.DependedOnCount > 1)
                {
                    Debug.LogWarning($"资源:{build.Key}被多个资源依赖{groupName}");
                    if (groupInfos.TryGetValue(groupName, out var groupInfo))
                    {
                        groupInfo.GetOrCreateEntry(build.Key, build.Value.GetAddressPath());
                    }
                }
            }

            foreach (var groupInfo in groupInfos)
            {
                groupInfo.Value.Clear();
            }
        }

        private string GetAddressGroupName(IAssetDependency  dependency)
        {
            AssetbundleClassify classify = CurrentAssetBundlePath.GetLoadType(dependency.AssetPath)??CurrentAssetBundlePath.GetDependClassify(dependency.LoadType);

            if (classify != null)
            {
                return classify.AssetbundleRealityPath;
            }
            return "Err";
        }

        class GroupInfo
        {
            public GroupInfo(AddressableAssetSettings settings, string groupName,
                BundledAssetGroupSchema.BundlePackingMode bundleMode =
                    BundledAssetGroupSchema.BundlePackingMode.PackTogether)
            {
                GroupName = groupName;
                this._bundleMode = bundleMode;
                _settings = settings;
            }

            public string GroupName;
            private AddressableAssetGroup _group;
            private readonly AddressableAssetSettings _settings;
            private readonly BundledAssetGroupSchema.BundlePackingMode _bundleMode;
            private HashSet<AddressableAssetEntry> _addressHash = new HashSet<AddressableAssetEntry>();

            private void GetOrCreateGroup()
            {
                _group = _settings.FindGroup(GroupName);
                var bundledAssetGroupSchema =
                    ScriptableObject.CreateInstance<BundledAssetGroupSchema>();
                bundledAssetGroupSchema.BundleMode = _bundleMode;
                bundledAssetGroupSchema.BundleNaming = BundledAssetGroupSchema.BundleNamingStyle.OnlyHash;
                bundledAssetGroupSchema.UseAssetBundleCrc = false;
                bundledAssetGroupSchema.UseAssetBundleCache = false;

                var contentUpdateGroupSchema =
                    ScriptableObject.CreateInstance<ContentUpdateGroupSchema>();
                if (_group == null)
                    _group = _settings.CreateGroup(GroupName, false, false, true,
                        new List<AddressableAssetGroupSchema>
                        {
                            bundledAssetGroupSchema,
                            contentUpdateGroupSchema
                        });
                _addressHash = _group.entries.ToHashSet();
            }

            public void GetOrCreateEntry(
                string path,
                string address)
            {
                if (_group == null)
                {
                    GetOrCreateGroup();
                }

                var guid = AssetDatabase.AssetPathToGUID(path);
                var entry = _group.GetAssetEntry(guid, false);
                if (entry == null)
                {
                    entry = _settings.CreateOrMoveEntry(guid, _group, false, false);
                }
                else
                {
                    _addressHash.Remove(entry);
                }

                entry.address = address;
            }

            public void Clear()
            {
                if (_group == null)
                {
                    return;
                }

                foreach (var address in _addressHash)
                {
                    _group.RemoveAssetEntry(address);
                }
            }
        }

        private Dictionary<string, GroupInfo> GetGroupInfos(AddressableAssetSettings settings,
            BundledAssetGroupSchema.BundlePackingMode bundleMode =
                BundledAssetGroupSchema.BundlePackingMode.PackSeparately)
        {
            List<string> directoryNames = CurrentAssetBundlePath.GetGroupNames();
            Dictionary<string, GroupInfo> groupInfos = new Dictionary<string, GroupInfo>();
            foreach (var directoryName in directoryNames)
            {
                groupInfos.Add(directoryName, new GroupInfo(settings, directoryName, bundleMode));
                string temp = $"{directoryName}{dependencyName}";
                groupInfos.Add(temp, new GroupInfo(settings, temp, bundleMode));
            }

            return groupInfos;
        }

        private void SetLabel(AddressableAssetSettings settings, AddressableAssetEntry entry, string label)
        {
            settings.AddLabel(label, false);
            entry.SetLabel(label, true, false, false);
        }
    }
}